package net.documentshare.ftp;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;

import net.documentshare.utils.StringsUtils;

import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;

public class FtpSession implements Serializable {
	private static final long serialVersionUID = 866208930146190113L;
	public final FTPClient client;
	public final FtpConfig config;

	public FtpSession(final FtpConfig config) {
		this.config = config;
		client = new FTPClient();
	}

	/**
	 * 测试ftp是否可以连接
	 * @return
	 */
	public boolean testConnnected() {
		boolean conn = false;
		try {
			checkOpened();
			conn = client.isConnected();
			release();
		} catch (final Exception e) {
		}
		return conn;
	}

	private final void checkOpened() throws IOException {
		if (client.isConnected()) {
			client.disconnect();
			//			return;
		}
		client.connect(config.host, config.port);
		client.login(config.user, config.pass);
		client.changeWorkingDirectory(config.path);
		client.setFileType(FTP.BINARY_FILE_TYPE);
	}

	public void makeDirectory(final String path) {
		try {
			checkOpened();
			client.mkd(path);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public void changeWorkingDirectory(final String path) throws IOException {
		if (client.isConnected()) {
			client.changeWorkingDirectory(path);
			return;
		}
		client.connect(config.host, config.port);
		client.setFileType(FTP.BINARY_FILE_TYPE);
		client.login(config.user, config.pass);
		client.changeWorkingDirectory(config.path);
	}

	public InputStream openInStream() throws IOException {
		return openInStream(config.name);
	}

	public InputStream openInStream(final String path, final String file) throws IOException {
		return openInStream(path + '/' + file);
	}

	public InputStream openInStream(final String filePath) throws IOException {
		checkOpened();
		return client.retrieveFileStream(filePath);
	}

	public OutputStream openOutStream() throws IOException {
		return openOutStream(config.name);
	}

	public OutputStream openOutStream(final String path, final String file) throws IOException {
		return openOutStream(path + '/' + file);
	}

	public OutputStream openOutStream(final String filePath) throws IOException {
		checkOpened();
		return client.storeFileStream(filePath);
	}

	public boolean putFile(final InputStream srcInStream, final String targetFile) throws IOException {
		checkOpened();
		return client.storeFile(targetFile, srcInStream);
	}

	public OutputStream putOutStream(final String targetFile) throws IOException {
		checkOpened();
		return client.storeFileStream(targetFile);
	}

	public Collection<String> listDirectory() throws IOException {
		return listDirectory("");
	}

	public Collection<String> listDirectory(final String path) throws IOException {
		checkOpened();
		final Collection<String> filePaths = new ArrayList<String>(8);
		final FTPFile[] ftpFiles = client.listFiles(path);
		for (final FTPFile ftpFile : ftpFiles) {
			if ("..".equals(ftpFile.getName()) || ".".equals(ftpFile.getName()))
				continue;
			if (ftpFile.isDirectory())
				filePaths.add("".equals(path) ? ftpFile.getName() : StringsUtils.u(path, '/', ftpFile.getName()));
		}
		return filePaths;
	}

	public Collection<FtpFileWrapper> listDirectoryFiles() throws IOException {
		return listDirectoryFiles("");
	}

	public Collection<FtpFileWrapper> listDirectoryFiles(final String path) throws IOException {
		checkOpened();
		final Collection<FtpFileWrapper> filePaths = new ArrayList<FtpFileWrapper>(8);
		final FTPFile[] ftpFiles = client.listFiles(path);
		for (final FTPFile ftpFile : ftpFiles) {
			if ("..".equals(ftpFile.getName()) || ".".equals(ftpFile.getName()))
				continue;
			if (ftpFile.isDirectory()) {
				final FtpFileWrapper file = new FtpFileWrapper();
				file.fileName = ftpFile.getName();
				file.filePath = "".equals(path) ? ftpFile.getName() : StringsUtils.u(path, '/', ftpFile.getName());
				file.lastModifiedTime = ftpFile.getTimestamp().getTimeInMillis();
				file.fileSize = ftpFile.getSize();
				filePaths.add(file);
			}
		}
		return filePaths;
	}

	public Collection<String> listFilesPath() throws IOException {
		return listFilesPath("");
	}

	public Collection<String> listFilesPath(final String path) throws IOException {
		checkOpened();
		final Collection<String> filePaths = new ArrayList<String>(8);
		final FTPFile[] ftpFiles = client.listFiles(path);
		for (final FTPFile ftpFile : ftpFiles) {
			if ("..".equals(ftpFile.getName()) || ".".equals(ftpFile.getName()))
				continue;
			if (ftpFile.isDirectory())
				continue;// skip ,will fix
			filePaths.add(StringsUtils.u(config.path, '/', path, '/', ftpFile.getName()));
		}
		return filePaths;
	}

	public Collection<FtpConfig> listFiles() throws IOException {
		return listFiles("");
	}

	public Collection<FtpConfig> listFiles(final String path) throws IOException {
		checkOpened();
		final Collection<FtpConfig> filePaths = new ArrayList<FtpConfig>(8);
		final FTPFile[] ftpFiles = client.listFiles(path);
		final String curDir = StringsUtils.u(config.path, '/', path);
		for (final FTPFile ftpFile : ftpFiles) {
			if ("..".equals(ftpFile.getName()) || ".".equals(ftpFile.getName()))
				continue;
			if (ftpFile.isDirectory())
				continue;// skip ,will fix
			//考虑是局域网因素,判断读取到的当前文件的最后修改时间和当前时间相差多少
			//如果小于2秒，表示文件还在写，暂不做处理
			if (config.validateFileSize) {
				final long localCurrentTime = System.currentTimeMillis();
				final long ftpLastM = ftpFile.getTimestamp().getTimeInMillis();
				try {
					if ((localCurrentTime - ftpLastM) <= 2000) {
						continue;
					}
				} catch (final Exception e) {
					//debug
				}
			}
			filePaths.add(new FtpConfig(config.host, config.port, config.user, config.pass, curDir, ftpFile.getName()));
		}
		return filePaths;
	}

	/**
	 * 重新命名文件
	 * 
	 * @param newName
	 *            给定的新文件名
	 * @return
	 * @throws IOException
	 */
	public boolean rename(final String newName) throws IOException {
		checkOpened();
		return this.client.rename(config.name, newName);
	}

	/**
	 * 重新命名文件,将给定的扩展名加在原来文件名的后面
	 * 
	 * @return
	 * @throws IOException
	 */
	public boolean renameByAdd(final String ext) throws IOException {
		try {
			return rename(config.name + ext);
		} finally {
			release();
		}
	}

	/**
	 * 根据给定的扩展名恢复文件名,只有在当前文件名存在给定的ext后缀时才执行操作
	 * 
	 * @param ext
	 * @return
	 * @throws IOException
	 */
	public boolean renameByDel(final String ext) throws IOException {
		if (!config.name.endsWith(ext))
			return false;
		try {
			return rename(config.name.substring(0, config.name.length() - ext.length()));
		} finally {
			release();
		}
	}

	public void release() {
		try {
			if (this.client.isConnected())
				client.disconnect();
		} catch (final Exception e) {
		}
	}
}
